#!rsc by RouterOS
# RouterOS script: mod/notification-gotify
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
#                         Leonardo David Monteiro <leo@cub3.xyz>
# https://rsc.eworm.de/COPYING.md
#
# requires RouterOS, version=7.15
# requires device-mode, fetch, scheduler
#
# send notifications via Gotify (gotify.net)
# https://rsc.eworm.de/doc/mod/notification-gotify.md

:global FlushGotifyQueue;
:global NotificationFunctions;
:global PurgeGotifyQueue;
:global SendGotify;
:global SendGotify2;

# flush Gotify queue
:set FlushGotifyQueue do={ :onerror Err {
  :global GotifyQueue;

  :global IsFullyConnected;
  :global LogPrint;

  :if ([ $IsFullyConnected ] = false) do={
    $LogPrint debug $0 ("System is not fully connected, not flushing.");
    :return false;
  }

  :local AllDone true;
  :local QueueLen [ :len $GotifyQueue ];

  :if ([ :len [ /system/scheduler/find where name="_FlushGotifyQueue" ] ] > 0 && $QueueLen = 0) do={
    $LogPrint warning $0 ("Flushing Gotify messages from scheduler, but queue is empty.");
  }

  :foreach Id,Message in=$GotifyQueue do={
    :if ([ :typeof $Message ] = "array" ) do={
      :onerror Err {
        /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
          http-header-field=($Message->"headers") http-data=[ :serialize to=json ($Message->"message") ] \
          ($Message->"url") as-value;
        :set ($GotifyQueue->$Id);
      } do={
        $LogPrint debug $0 ("Sending queued Gotify message failed: " . $Err);
        :set AllDone false;
      }
    }
  }

  :if ($AllDone = true && $QueueLen = [ :len $GotifyQueue ]) do={
    /system/scheduler/remove [ find where name="_FlushGotifyQueue" ];
    :set GotifyQueue;
  }
} do={
  :global ExitError; $ExitError false $0 $Err;
} }

# send notification via Gotify - expects one array argument
:set ($NotificationFunctions->"gotify") do={
  :local Notification $1;

  :global Identity;
  :global IdentityExtra;
  :global GotifyQueue;
  :global GotifyServer;
  :global GotifyServerOverride;
  :global GotifyToken;
  :global GotifyTokenOverride;

  :global EitherOr;
  :global FetchUserAgentStr;
  :global IfThenElse;
  :global LogPrint;
  :global SymbolForNotification;

  :local Server [ $EitherOr ($GotifyServerOverride->($Notification->"origin")) $GotifyServer ];
  :local Token [ $EitherOr ($GotifyTokenOverride->($Notification->"origin")) $GotifyToken ];

  :if ([ :len $Token ] = 0) do={
    :return false;
  }

  :local Url ("https://" . $Server . "/message");
  :local Headers ({ [ $FetchUserAgentStr ($Notification->"origin") ]; \
    ("X-Gotify-Key: " . $Token); "Content-Type: application/json" });
  :local Message ({
    "title"=("[" . $IdentityExtra . $Identity . "] " . ($Notification->"subject")); \
    "message"=(($Notification->"message") . "\n" . [ $IfThenElse ([ :len ($Notification->"link") ] > 0) \
      ("\n" . [ $SymbolForNotification "link" ] . ($Notification->"link")) ]); \
    "priority"=[ :tonum [ $IfThenElse ($Notification->"silent") 2 5 ] ] });

  :onerror Err {
    /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
      http-header-field=$Headers http-data=[ :serialize to=json $Message ] $Url as-value;
  } do={
    $LogPrint info $0 ("Failed sending Gotify notification: " . $Err . " - Queuing...");

    :if ([ :typeof $GotifyQueue ] = "nothing") do={
      :set GotifyQueue ({});
    }
    :set ($Message->"message") (($Notification->"message") . "\n" . \
      [ $SymbolForNotification "alarm-clock" ] . "This message was queued since " . \
      [ /system/clock/get date ] . " " . [ /system/clock/get time ] . " and may be obsolete.");
    :set ($GotifyQueue->[ :len $GotifyQueue ]) \
      { url=$Url; headers=$Headers; message=$Message };
    :if ([ :len [ /system/scheduler/find where name="_FlushGotifyQueue" ] ] = 0) do={
      /system/scheduler/add name="_FlushGotifyQueue" interval=1m start-time=startup \
        on-event=(":global FlushGotifyQueue; \$FlushGotifyQueue;");
    }
  }
}

# purge the Gotify queue
:set PurgeGotifyQueue do={
  :global GotifyQueue;

  /system/scheduler/remove [ find where name="_FlushGotifyQueue" ];
  :set GotifyQueue;
}

# send notification via Gotify - expects at least two string arguments
:set SendGotify do={ :onerror Err {
  :global SendGotify2;

  $SendGotify2 ({ origin=$0; subject=$1; message=$2; link=$3; silent=$4 });
} do={
  :global ExitError; $ExitError false $0 $Err;
} }

# send notification via Gotify - expects one array argument
:set SendGotify2 do={
  :local Notification $1;

  :global NotificationFunctions;

  ($NotificationFunctions->"gotify") ("\$NotificationFunctions->\"gotify\"") $Notification;
}
